package com.zjpavt.client.util;


import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
import java.util.ArrayList;
import java.util.List;

/**
 * 文件读取获取工具类
 */
public class FileUtil {

    private static final Logger log = LoggerFactory.getLogger(FileUtil.class);

    /**
     * 读取文件到String
     *
     * @param filePath 路径
     * @return 读取到的String
     */
    public static String readFromFile(String filePath) throws IOException {
        File file = new File(filePath);
        if (!file.exists()) {
            log.error(filePath + "not exist");
            throw new FileNotFoundException(filePath + "not exist");
        }
        return FileUtils.readFileToString(new File(filePath), ClientConfig.DEFAULT_ENCODING);
    }

    /**
     * 写入String到文件
     *
     * @param filePath 路径
     * @param msg  消息
     */
    public static void writeToFile(String filePath, String msg) throws IOException {
        FileUtils.writeStringToFile(new File(filePath), msg, ClientConfig.DEFAULT_ENCODING);
    }

    /**
     * 读取文件到String
     *
     * @param filePath 路径
     * @return 读取到的String
     */
    public static String readFromFileSafe(String filePath) throws IOException {
        File file = new File(filePath);
        FileInputStream in = null;
        FileLock lock = null;
        FileChannel channel = null;
        StringBuilder res = new StringBuilder();
        try {
            if (file.exists()) {
                if (file.isDirectory()) {
                    throw new IOException("File '" + file + "' exists but is A directory");
                } else if (!file.canRead()) {
                    throw new IOException("File '" + file + "' cannot be read");
                } else {
                    in = new FileInputStream(file);
                }
            } else {
                throw new FileNotFoundException("File '" + file + "' does not exist");
            }
            channel = in.getChannel();
            if (channel != null) {
                //文件不可写时无法获取独占锁     （有参数为共享锁）
                lock = channel.lock(0L, Long.MAX_VALUE, true);
                //读的时候开共享锁 写的时候开独占锁
                log.debug("read lock get");
            } else {
                throw new IOException("fail to get channel");
            }
            ByteBuffer byteData = ByteBuffer.allocate(1024);
            byte[] bytes = new byte[1024];
            int len;
            while ((len = channel.read(byteData)) != -1) {
                byteData.flip();
                if (len != 1024) {
                    bytes = new byte[len];
                    byteData.get(bytes, 0, len);
                }
                byteData.clear();
                res.append(new String(bytes));
            }
        }
        finally {
            if (lock != null) {
                lock.release();
                lock = null;
                log.debug("read lock release");
            }
            if (channel != null) {
                channel.close();
            }
            IOUtils.closeQuietly(in);
        }
        return res.toString();
    }

    /**
     * 写入String到文件
     *
     * @param filePath 路径
     * @param msg 写入消息
     */
    public static void writeToFileSafe(String filePath, String msg) throws IOException {
        RandomAccessFile out = null;
        File file = new File(filePath);
        FileLock lock = null;
        FileChannel channel = null;
        try {
            if (file.exists()) {
                if (file.isDirectory()) {
                    throw new IOException("File '" + file + "' exists but is A directory");
                }
                if (!file.canWrite()) {
                    throw new IOException("File '" + file + "' cannot be written to");
                }
            } else {
                File parent = file.getParentFile();
                if (parent != null && !parent.mkdirs() && !parent.isDirectory()) {
                    throw new IOException("Directory '" + parent + "' could not be created");
                }
            }
            out = new RandomAccessFile(file, "rw");
            channel = out.getChannel();
            lock = channel.lock();
            log.debug("write lock get");
            channel.write(ByteBuffer.wrap(msg.getBytes()));
        }
        finally {
            if (lock != null) {
                lock.release();
                lock = null;
                log.debug("write lock release");
            }
            if (channel != null) {
                channel.close();
            }
            IOUtils.closeQuietly(out);
        }

    }

    /**
     * 读取某个文件的某一行
     *
     * @param filePath 路径
     * @param lineNum 第几行
     */
    public static String readFileByLineNum(String filePath, int lineNum) throws IOException, IllegalStateException {
        BufferedReader br = null;
        try {
            br = new BufferedReader(new FileReader(new File(filePath)));
            int count = 1;
            while (count != lineNum) {
                if (br.readLine() == null) {
                    throw new IOException("beyond file line number");
                }
                count++;
            }
            String result = br.readLine();
            if (result == null) {
                throw new IOException("beyond file line number");
            }
            return result;
        }
        finally {
            if (br != null) {
                br.close();
            }
        }
    }

    /**
     * @param filePath 文件路径
     * @param reg      匹配当前正则的文件名
     */
    public static List<String> getChildFilesName(String filePath, String reg) {
        List<String> result = new ArrayList<>();
        File f = new File(filePath);
        File[] listFiles = f.listFiles();
        if (listFiles != null) {
            for (File file : listFiles) {
                if (file.getName().matches(reg)) {
                    result.add(file.getName());
                }
            }
        }
        return result;
    }

    /**
     *  当文件不存在的时候创建父文件夹 新建子文件
     * @param filePath
     * @return
     * @throws IOException
     */
    public static File makeSureFileExist(String filePath) throws IOException {
        File file2 = new File(filePath);
        File file = file2.getParentFile();
        if(!file2.exists()) {
            if(!file.exists()) {
                file.mkdirs();
            }
            file2.createNewFile();
        }
        return file2;
    }

    /**
     * 确认文件存在
     * @param file
     */
    public static boolean makeSureFileExist(File file) {
        try {
            if (!file.exists()) {
                if (!file.getParentFile().exists()) {
                    file.mkdirs();
                }
                if (!file.createNewFile()) {
                    throw new IOException();
                }
            } else if (file.exists() && !file.isFile()) {
                if (file.delete() && file.createNewFile()) {
                    throw new IOException();
                }
            }
        } catch (IOException e) {
            log.error("new file fail :" + ClientConfig.STORE_KEY_CODE);
            return false;
        }
        return true;
    }
}
